--
-- Maker Tool used to edit infotext of certain nodes
--
--
-- DONE: Add tool for editing infotexts on nodes
-- DONE: Add a priv to allow using the tool
-- DONE: Non-admin player can only edit node on empty infotext
-- DONE: Add a setting to whitelist some nodes
-- DONE: Add a setting to blacklist some nodes
-- DONE: Don't add wear if player has creative
-- DONE: Admin can also set owner to protect the node from being edited
-- DONE: Disalow editing for protected nodes
-- FIXME: Check for protection before opening form, not after
-- FIXME: Don't add wear if string hasn't changed
-- FIXME: Setting owner doesn't protect the node form being dug or replaced.


local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)

-- Load support for MT game translation.
local S = minetest.get_translator(modname)

-- Tool will only work on listed allowed nodes
-- -- unless allowed nodes list is empty,
-- it will then work on everything exept nodes listed
-- -- in the ignored nodes list
-- -- If both lists are empty it will work on anything not owned by someone else
-- But it won't work on node without infotext if marker_tool_restrict_create is true

--- List of allowed node
---- If left empty, players will be able to edit any node.
local allowed_nodes = {}

--- List of exeptions to the allowed nodes
local ignored_nodes = {}

csv_allowed = minetest.settings:get(modname .. "_allowed_nodes") or ""
csv_ignored = minetest.settings:get(modname .. "_ignored_nodes") or ""
restrict_create = minetest.settings:get(modname .. "_restrict_create") or false

function list_add_from_csv(list,str)
  local items = str:split(",")
  for _, itemname in ipairs(items) do
    give_initial_stuff.add(itemname)
  end
end

list_add_from_csv(allowed_nodes, csv_allowed)
list_add_from_csv(ignored_nodes, csv_ignored)

local marker_tool = {}
marker_tool.msg = {}

local modname_prefix = string.format("[%s] ", modname)
marker_tool.msg.priv_description = S("Able to edit nodes infotext with maker tool.")
marker_tool.msg.player_or_node_info_missing = modname_prefix .. S("Maker pen was used but player name or node infos were not found.")
marker_tool.msg.node_name_missing = modname_prefix .. S("Maker pen was used but node name was not found.")
marker_tool.msg.not_editable = S("This node cannot be edited with the marker pen")
marker_tool.msg.priv_missing = S("You need the 'infotext_editor' priv to use the marker pen")
marker_tool.msg.not_the_owner = S("%s is the owner of this node, you are not allowed to modify it")
marker_tool.msg.not_the_door_owner = S("%s is the owner of this door, you are not allowed to modify it")
marker_tool.msg.label_owner = S("Owner")
marker_tool.msg.button_write = S("Write")
marker_tool.msg.log_write_success = modname_prefix .. S("%s wrote '%s' at %s")
marker_tool.msg.tool_description = S("Marker")

local msg = marker_tool.msg

minetest.register_privilege("infotext_editor", {description = msg.priv_description, give_to_singleplayer=true})

local function node_is_allowed(node_name)
  local n = node_name
  if ignored_nodes
    and type(ignored_nodes) == "table"
    and #ignored_nodes > 0
    then
      for _, v in pairs(ignored_nodes) do
        if v == n then return false end
      end
  end

  if allowed_nodes
    and type(allowed_nodes) == "table"
    then
      if #allowed_nodes > 0
      then
        for _, v in pairs(allowed_nodes) do
          if v == n then return true end
        end
      else
        return true
      end
  end

  return false
end


local function player_is_admin(name)
 return minetest.check_player_privs(name, {privs=true})
end

local function player_is_editor(name)
 return minetest.check_player_privs(name, {infotext_editor=true})
end

-- Check if this node can be edited (function)
local function can_edit(name,pos,meta)

  -- Safety check
  if (not name) or (not pos) or (not meta) then
    minetest.log("error", msg.player_or_node_info_missing)
    return false
  end

  local node = minetest.get_node_or_nil(pos)
  if not node.name then
    minetest.log("error", msg.node_name_missing)
    return false
  end

  local player_has_editor_priv = player_is_editor(name)
  -- Players can edit anything if they are admin and editor
  if player_has_editor_priv and player_is_admin(name) then return true end

  -- Non-admin players are not allowed to edit nodes belonging to someone else
  local owner = meta:get_string("owner") or meta:get_string("doors_owner")

  if owner and owner ~= '' and owner ~= name then
    minetest.chat_send_player(name, msg.not_the_owner:format(owner))
    return false
  end

  -- Players can edit anything not owned by someone else if they are editor
  if player_has_editor_priv then return true end

  -- Without any special priv, players can edit any node allowed by settings
  if not node_is_allowed(node.name) then
    minetest.chat_send_player(name, msg.not_editable)
    return false
  end

  -- If restrict_create setting is set, players can only edit exising infotexts, not create new ones...
  if restrict_create then
    -- Return if node does not have infotext and player is not editor
    local infotext = meta:get_string("infotext")
    -- Otherwise editors can only edit allowed node that and they must already have a non empty infotext
    if not ( infotext and infotext ~= "" ) then
      minetest.chat_send_player(name, msg.not_editable)
      return false
    end
  end

  -- If everything checked return true
  return true
end

--
-- What happen when tool is used
--
local function marker_tool_onuse(itemstack,player,pointed_thing,uses)

  -- Return if not a node
  if pointed_thing.type~="node" then return false end

  -- Get player name
  local player_name = player:get_player_name()

  -- Get node metadata
  local p = pointed_thing.under
  local meta = minetest.get_meta(p)


  -- Check if this node can be edited (function)
  if not can_edit(player_name,p,meta) then return end

  -- Get node infotext
  local infotext = meta:get_string("infotext") or ""


  -- Build formspec
  local formspec = "size[6,4]"

  if player_is_admin(player_name) then
    local owner = meta:get_string("owner")
    if not owner or owner == "" then
      owner = player_name
    end
    formspec = formspec ..
        "textarea[0.3,0;6,2.5;infotext;;"..infotext.."]"..
        "field[0.3,3;6,0.5;owner;"..msg.label_owner..";"..owner.."]"
  else
    formspec = formspec ..
        "textarea[0.3,0;6,3;infotext;;"..infotext.."]"

  end

  -- Display formspec
 local formspec = formspec ..
                'field[0,-3;2,1;pos;;'.. minetest.pos_to_string(p)..']'..
                "button_exit[2,3.4;2,1;ok;".. msg.button_write.."]"..
                default.gui_bg..
                default.gui_bg_img

  minetest.show_formspec(player_name,modname..":marker_formspec",formspec)

  if not minetest.is_creative_enabled(player_name)
    then
      itemstack:add_wear(65535 / ((uses or 20) - 1))
  end
  return itemstack
end

-- Save informations from form
minetest.register_on_player_receive_fields(function(player, form, fields)
  -- Return if not the right form
  if form ~= modname..":marker_formspec" or not fields.infotext or not fields.pos then
   return
  end
  local pos = minetest.string_to_pos(fields.pos)
  -- Make sure node was not protected
  local name = player:get_player_name()
  if minetest.is_protected(pos, name) then
    minetest.record_protection_violation(pos, name)
    return
  end
  -- Update meta an inform log
  local meta = minetest.get_meta(pos)

  minetest.log("action", msg.log_write_success:format(name,fields.infotext,fields.pos))
  meta:set_string("infotext",fields.infotext)
  if fields.owner and fields.owner ~= "" and player_is_admin(name) then
    meta:set_string("owner",fields.owner)
  end
end)



-- Register the tool
minetest.register_tool(modname..":marker", {
  description = msg.tool_description,
  inventory_image = "marker.png",
  -- range = 2,
  -- groups = {not_in_creative_inventory=1},
  on_use = function(itemstack, player, pointed_thing)
    marker_tool_onuse(itemstack,player,pointed_thing,20)
    return itemstack
  end,
  on_place = function(itemstack, player, pointed_thing)
    marker_tool_onuse(itemstack,player,pointed_thing,20)
    return itemstack
  end,
})

-- Recipes (only if dye mod exist)
if minetest.get_modpath('dye') then
  minetest.register_craft({
    output = modname..":marker",
    recipe = {
      {'','dye:black'},
      {'group:stick',''},
    }
  })
else
  minetest.register_craft({
    output = modname..":marker",
    recipe = {
      {'','default:coal_lump'},
      {'group:stick',''},
    }
  })
end
